package com.chencl.cipher.util;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.apache.tomcat.util.codec.binary.Base64;

/**
 * 
 * @ClassName:  RSAUtils   
 * @Description:TODO(描述这个类的作用)   
 * @author: Ccl
 * @date:   2021年11月25日 上午11:30:56      
 * @Copyright:
 */
public class RSAUtils {
	//算法名称
	public static final String KEY_ALGORITHM = "RSA";
	//签名算法
	public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
	//密钥长度
	private static final int KEY_SIZE=1024;

	/**
	 * 
	 * @Title: genKeyPair   
	 * @Description: 获取公私秘钥
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: Map<String,Object>      
	 * @throws
	 */
	public static KeyPair genKeyPair() throws Exception {
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		/*//安全随机数
		byte[] salt = new byte[128];
		SecureRandom random = new SecureRandom();
		random.setSeed(System.currentTimeMillis());
		random.nextBytes(salt);
		keyPairGen.initialize(KEY_SIZE, random);*/
		keyPairGen.initialize(KEY_SIZE);
		return keyPairGen.generateKeyPair();
	}
	
	/**
	 * 
	 * @Title: getPrivateKey   
	 * @Description: 获取秘钥  
	 * @param: @param keyPair
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: String      
	 * @throws
	 */
	public static String getPrivateKey(KeyPair keyPair) throws Exception {
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		return Base64Utils.encode(privateKey.getEncoded());
	}

	/**
	 * 
	 * @Title: getPrivateKey   
	 * @Description: 获取公钥  
	 * @param: @param keyPair
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: String      
	 * @throws
	 */
	public static String getPublicKey(KeyPair keyPair) throws Exception {
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		return Base64Utils.encode(publicKey.getEncoded());
	}
	
	/**
	 * 发送方使用自己的私钥加密数字签名,防抵赖
	 * @Title: sign   
	 * @Description: （生成数字签名）私钥加密  
	 * @param: @param data 数据源二进制数组
	 * @param: @param privateKey 私钥
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: String      
	 * @throws
	 */
	public static String sign(byte[] data, String privateKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(privateK);
		signature.update(data);
		return Base64Utils.encode(signature.sign());
	}

	/**
	 * 接收方用发送发的公钥解密数字签名
	 * @Title: verify   
	 * @Description: （生成数字签名）公钥解密 
	 * @param: @param data 数据源二进制数组
	 * @param: @param publicKey 公钥
	 * @param: @param sign
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: boolean      
	 * @throws
	 */
	public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
		byte[] keyBytes = Base64Utils.decode(publicKey);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PublicKey publicK = keyFactory.generatePublic(keySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initVerify(publicK);
		signature.update(data);
		return signature.verify(Base64Utils.decode(sign));
	}

	/**
	 * 
	 * @Title: decryptByPrivateKey   
	 * @Description: 私钥加密数据
	 * @param: @param encryptedData 数据源二进制数组
	 * @param: @param privateKey 私钥
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: byte[]      
	 * @throws
	 */
	public static String decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(2, privateK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		int i = 0;
		while (inputLen - offSet > 0) {
			byte[] cache;
			if (inputLen - offSet > 128)
				cache = cipher.doFinal(encryptedData, offSet, 128);
			else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * 128;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return new String(decryptedData,"UTF-8");
	}

	/**
	 * 
	 * @Title: decryptByPublicKey   
	 * @Description: 公钥解密数据
	 * @param: @param encryptedData 数据源二进制数组
	 * @param: @param publicKey 公钥
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: byte[]      
	 * @throws
	 */
	public static String decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(2, publicK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		int i = 0;
		while (inputLen - offSet > 0) {
			byte[] cache;
			if (inputLen - offSet > 128)
				cache = cipher.doFinal(encryptedData, offSet, 128);
			else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * 128;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return new String(decryptedData,"UTF-8");
	}

	/**
	 * 
	 * @Title: encryptByPublicKey   
	 * @Description: 公钥加密
	 * @param: @param data 数据源二进制数组
	 * @param: @param publicKey 公钥
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: byte[]      
	 * @throws
	 */
	public static String encryptByPublicKey(byte[] data, String publicKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(1, publicK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		int i = 0;
		while (inputLen - offSet > 0) {
			byte[] cache;
			if (inputLen - offSet > 117)
				cache = cipher.doFinal(data, offSet, 117);
			else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * 117;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return new String(Base64.encodeBase64(encryptedData), "UTF-8");
	}

	/**
	 * 
	 * @Title: encryptByPrivateKey   
	 * @Description: 私钥解密
	 * @param: @param data 数据源二进制数组
	 * @param: @param privateKey 私钥
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: byte[]      
	 * @throws
	 */
	public static String encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(1, privateK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		int i = 0;
		while (inputLen - offSet > 0) {
			byte[] cache;
			if (inputLen - offSet > 117)
				cache = cipher.doFinal(data, offSet, 117);
			else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * 117;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return new String(Base64.encodeBase64(encryptedData), "UTF-8");
	}

	public static Map<String,String> getRsaKey(){
		try {
			Map<String,String> retMap = new HashMap<>();
			KeyPair keyPair = genKeyPair();
			String publiceKey = getPublicKey(keyPair);
			String privateKey = getPrivateKey(keyPair);
			retMap.put("publiceKey", publiceKey);
			retMap.put("privateKey", privateKey);
			return retMap;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public static void main(String[] args) {
		try {
			System.out.println("reaKey:"+getRsaKey());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}